﻿using MvCamCtrl.NET;
using OpenCvSharp;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static MvCamCtrl.NET.MyCamera;

namespace BaseTool
{
    /// <summary>
    /// 相机工具类
    /// </summary>
    public class CameraTool
    {
        /// <summary> 保存文件名称 </summary>
        public string SaveFileName = "Camera.xml";
        /// <summary> 相机参数 </summary>
        public CameraParam Param = new CameraParam();
        /// <summary> 相机采集（异步） </summary>
        public CameraCapture Capture = new CameraCapture();

        /// <summary> 是否已连接设备 </summary>
        public bool IsConnected { private set; get; } = false;
        /// <summary> 取流标志位 </summary>
        public bool IsGrabbing { private set; get; } = false;

        /// <summary> 相机句柄 </summary>
        private MyCamera myCamera = new MyCamera();
        /// <summary> 后台采集线程 </summary>
        private Thread threadReceive = null;

        /// <summary> 内存复制 </summary>
        [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
        private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

        #region 参数读取&保存
        /// <summary> 构造 </summary>
        public CameraTool()
        {

        }

        /// <summary> 构造（多目预留） </summary>
        public CameraTool(string saveName)
        {
            SaveFileName = saveName;
        }

        /// <summary>
        /// 读取相机参数
        /// </summary>
        /// <param name="CameraXml">读取路径，空为Alltype中的配置</param>
        /// <returns></returns>
        public bool Read(string CameraXml = null)
        {
            try
            {
                string path = string.IsNullOrWhiteSpace(CameraXml) ? PathTool.CurrentProductPath + SaveFileName : CameraXml;
                if (!File.Exists(path))
                {
                    Param = new CameraParam();
                    Save();
                }

                Param = (CameraParam)FileTool.ReadXML(path, typeof(CameraParam));
                Log.SaveLog($"读取相机参数成功", LogType.Run, Color.Black);
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"读取相机参数异常，{ex.Message}", LogType.Run);
                return false;
            }
        }

        /// <summary>
        /// 写入相机参数
        /// </summary>
        /// <param name="CameraXml"></param>
        /// <returns></returns>
        public bool Save(string CameraXml = null)
        {
            try
            {
                string path = string.IsNullOrWhiteSpace(CameraXml) ? PathTool.CurrentProductPath + SaveFileName : CameraXml;
                bool isok = FileTool.WriteXML(path, Param);
                Log.SaveLog($"保存相机参数{(isok ? "成功" : "失败")}", LogType.Run, isok ? Color.Black : Color.Red);
                return isok;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"保存相机参数异常，{ex.Message}", LogType.Run);
                return false;
            }
        }
        #endregion

        #region 相机基础操作
        /// <summary>
        /// 1.枚举设备
        /// </summary>
        /// <returns></returns>
        public string[] RefreshDeviceList()
        {
            MyCamera.MV_CC_DEVICE_INFO_LIST DeviceList;
            List<string> DeviceListString;
            RefreshDeviceList(out DeviceList, out DeviceListString);
            return DeviceListString.ToArray();
        }

        /// <summary>
        /// 1.枚举设备
        /// </summary>
        /// <param name="DeviceList"></param>
        /// <param name="DeviceListString"></param>
        public void RefreshDeviceList(out MyCamera.MV_CC_DEVICE_INFO_LIST DeviceList, out List<string> DeviceListString)
        {
            // 创建设备列表 
            DeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
            DeviceListString = new List<string>();
            DeviceList.nDeviceNum = 0;
            int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref DeviceList);
            //枚举设备失败!
            if (nRet != 0)
                return;

            // 在窗体列表中显示设备名 
            for (int i = 0; i < DeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(DeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));

                    if (gigeInfo.chUserDefinedName != "")
                        DeviceListString.Add($"GEV: {gigeInfo.chUserDefinedName} ({gigeInfo.chSerialNumber})");
                    else
                        DeviceListString.Add($"GEV: {gigeInfo.chManufacturerName} {gigeInfo.chModelName} ({gigeInfo.chSerialNumber})");
                }
                else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));

                    if (usbInfo.chUserDefinedName != "")
                        DeviceListString.Add($"U3V: {usbInfo.chUserDefinedName} ({usbInfo.chSerialNumber})");
                    else
                        DeviceListString.Add($"U3V: {usbInfo.chManufacturerName} {usbInfo.chModelName} ({usbInfo.chSerialNumber})");
                }
            }
        }

        /// <summary>
        /// 2.连接相机
        /// </summary>
        public string ConnectedCamera()
        {

            try
            {
                // 枚举设备
                MyCamera.MV_CC_DEVICE_INFO_LIST DeviceList;
                List<string> DeviceListString;
                RefreshDeviceList(out DeviceList, out DeviceListString);

                int selectIndex = DeviceListString.IndexOf(Param.Name);

                if (DeviceList.nDeviceNum == 0 || selectIndex == -1)
                    return $"连接相机失败，相机列表为空或者未选择相机！";

                // 获取选择的设备信息 
                MyCamera.MV_CC_DEVICE_INFO device =
                    (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(DeviceList.pDeviceInfo[selectIndex],
                                                                  typeof(MyCamera.MV_CC_DEVICE_INFO));

                // 打开设备
                if (myCamera == null)
                {
                    myCamera = new MyCamera();
                    if (myCamera == null)
                        return $"连接相机失败，无法定义相机句柄！";
                }

                int nRet = myCamera.MV_CC_CreateDevice_NET(ref device);
                if (MyCamera.MV_OK != nRet)
                    return $"连接相机失败，无法创建设备通道({ErrorMsg(nRet)})！";

                nRet = myCamera.MV_CC_OpenDevice_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    myCamera.MV_CC_DestroyDevice_NET();
                    return $"连接相机失败，无法打开设备通道({ErrorMsg(nRet)})！";
                }

                // 探测网络最佳包大小(只对GigE相机有效) 
                string errorMsg = "";
                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    int nPacketSize = myCamera.MV_CC_GetOptimalPacketSize_NET();
                    if (nPacketSize > 0)
                    {
                        nRet = myCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                        if (nRet != MyCamera.MV_OK)
                            errorMsg = $"，但设置数据包大小失败({ErrorMsg(nRet)})！";
                    }
                    else
                        errorMsg = $"，但获取包大小失败({ErrorMsg(nPacketSize)})！";
                }

                // 获取参数 
                GetParam();

                //设置参数标志位
                IsConnected = true;

                //设置状态栏显示和日志显示
                Log.SaveLog($"连接相机成功（{DeviceListString[selectIndex]}）", LogType.Sensor, Color.Gray);
                return $"连接相机成功（{DeviceListString[selectIndex]}）";
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"连接相机异常，{ex.Message}", LogType.Sensor);
                return $"连接相机异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 3.关闭相机
        /// </summary>
        public string CloseCamera()
        {
            try
            {
                if (IsConnected)
                {
                    StopGrab();

                    if (bufForDriver != IntPtr.Zero)
                        Marshal.Release(bufForDriver);

                    // 关闭设备 
                    myCamera.MV_CC_CloseDevice_NET();
                    myCamera.MV_CC_DestroyDevice_NET();
                }

                //设置状态栏显示和日志显示
                Log.SaveLog($"关闭相机成功", LogType.Sensor, Color.Gray);
                return $"关闭相机成功";
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"关闭相机异常，{ex.Message}", LogType.Sensor);
                return $"关闭相机异常，{ex.Message}";
            }
            finally
            {
                //设置参数标志位
                IsConnected = false;
            }
        }

        /// <summary>
        /// 设置连续模式
        /// </summary>
        public string SetContinuesMode()
        {
            try
            {
                myCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);
                Log.SaveLog($"设置连续模式成功", LogType.Sensor, Color.Gray);
                return "OK";
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"设置连续模式异常，{ex.Message}", LogType.Sensor);
                return $"设置连续模式异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 设置软触发模式
        /// </summary>
        public string SetSoftTrigger()
        {
            try
            {
                // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0;
                //           1 - Line1;
                //           2 - Line2;
                //           3 - Line3;
                //           4 - Counter;
                //           7 - Software;

                myCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
                myCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
                Log.SaveLog($"设置软触发模式成功", LogType.Sensor, Color.Gray);
                return "OK";
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"设置软触发模式异常，{ex.Message}", LogType.Sensor);
                return $"设置软触发模式异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 设置硬触发模式
        /// </summary>
        public string SetHardTrigger(CameraHardwareParam cameraParam)
        {
            try
            {
                // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0;
                //           1 - Line1;
                //           2 - Line2;
                //           3 - Line3;
                //           4 - Counter;
                //           7 - Software;

                myCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
                myCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)cameraParam.triggerSource);
                Log.SaveLog($"设置硬触发模式成功", LogType.Sensor, Color.Gray);
                return "OK";
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"设置硬触发模式异常，{ex.Message}", LogType.Sensor);
                return $"设置硬触发模式异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 4.开始采集（打开采集通道）
        /// </summary>
        public string StartGrab()
        {
            try
            {
                if (IsGrabbing)
                {
                    Log.SaveLog($"已经打开采集通道了", LogType.Sensor, Color.Gray);
                    return "已经打开采集通道了";
                }

                // 标志位置位true
                IsGrabbing = true;
                threadReceive = new Thread(ReceiveThreadProcess);
                threadReceive.Start();

                // 开始采集 
                int nRet = myCamera.MV_CC_StartGrabbing_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    IsGrabbing = false;
                    threadReceive.Join();
                    Log.SaveLog($"打开采集通道失败，错误码：{nRet}", LogType.Sensor, Color.Red);
                    return $"打开采集通道失败";
                }
                else
                {
                    Log.SaveLog($"打开采集通道成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"打开采集通道异常，{ex.Message}", LogType.Sensor);
                return $"打开采集通道异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 5.停止采集
        /// </summary>
        public string StopGrab()
        {
            try
            {
                if (!IsGrabbing)
                {
                    Log.SaveLog($"已经关闭采集通道了", LogType.Sensor, Color.Gray);
                    return "已经关闭采集通道了";
                }

                // 标志位设为false
                IsGrabbing = false;
                threadReceive.Join();

                // 停止采集
                int nRet = myCamera.MV_CC_StopGrabbing_NET();
                if (nRet != MyCamera.MV_OK)
                {
                    Log.SaveLog($"关闭采集通道失败，错误码：{nRet}", LogType.Sensor, Color.Red);
                    return $"关闭采集通道失败({ErrorMsg(nRet)})";
                }
                else
                {
                    Log.SaveLog($"关闭采集通道成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"关闭采集通道异常，{ex.Message}", LogType.Sensor);
                return $"关闭采集通道异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 软触发一次
        /// </summary>
        /// <param name="outImage"> 输出图像 </param>
        /// <param name="overTime"> 超时时间 默认3000ms </param>
        /// <returns></returns>
        public string TriggerExec(out Mat outImage, int overTime = 3000)
        {
            outImage = null;
            try
            {
                // 设置采集模式
                Capture.CaptureMode = CameraCaptureMode.软触发采集异步模式;
                while (Capture.ReceiveImage.Count != 0)
                {
                    Mat _temp;
                    Capture.ReceiveImage.TryDequeue(out _temp);
                }

                // 触发命令 
                int nRet = myCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
                if (nRet != MyCamera.MV_OK)
                {
                    string msg = $"软触发采集图像失败（{ErrorMsg(nRet)}）";
                    Log.SaveLog(msg, LogType.Sensor, Color.Red);
                    return msg;
                }

                //等待图像
                Stopwatch sw = Stopwatch.StartNew();
                while (Capture.ReceiveImage.Count == 0 && sw.ElapsedMilliseconds < overTime)
                {
                    Thread.Sleep(1);
                }
                if (Capture.ReceiveImage.Count == 0)
                {
                    string msg = $"软触发采集图像失败（采集超时，{sw.ElapsedMilliseconds}ms）";
                    Log.SaveLog(msg, LogType.Sensor, Color.Red);
                    return msg;
                }
                else
                {
                    Capture.ReceiveImage.TryDequeue(out outImage);
                    Log.SaveLog($"软触发采集图像成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"软触发采集图像异常，{ex.Message}", LogType.Sensor);
                return $"软触发采集图像异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 同步软触发，触发
        /// </summary>
        /// <returns></returns>
        public string SynchronousSoftTrigger()
        {
            try
            {
                // 设置采集模式
                Capture.CaptureMode = CameraCaptureMode.软触发采集异步模式;
                while (Capture.ReceiveImage.Count != 0)
                {
                    Mat _temp;
                    Capture.ReceiveImage.TryDequeue(out _temp);
                }

                // 触发命令 
                int nRet = myCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
                if (nRet != MyCamera.MV_OK)
                {
                    string msg = $"同步软触发采集图像，触发失败（{ErrorMsg(nRet)}）";
                    Log.SaveLog(msg, LogType.Sensor, Color.Red);
                    return msg;
                }
                else
                {
                    Log.SaveLog($"同步软触发采集图像，触发成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"同步软触发采集图像，触发异常，{ex.Message}", LogType.Sensor);
                return $"同步软触发采集图像，触发异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 同步软触发，获取
        /// </summary>
        /// <param name="outImage"></param>
        /// <param name="overTime"></param>
        /// <returns></returns>
        public string SynchronousSoftTrigger(out Mat outImage, int overTime = 3000)
        {
            outImage = null;
            try
            {
                //等待图像
                Stopwatch sw = Stopwatch.StartNew();
                while (Capture.ReceiveImage.Count == 0 && sw.ElapsedMilliseconds < overTime)
                {
                    Thread.Sleep(1);
                }
                if (Capture.ReceiveImage.Count != 0)
                {
                    Capture.ReceiveImage.TryDequeue(out outImage);
                    Log.SaveLog($"同步软触发采集图像，获取成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
                else
                {
                    string msg = $"同步软触发采集图像，获取失败（采集超时，{sw.ElapsedMilliseconds}ms）";
                    Log.SaveLog(msg, LogType.Sensor, Color.Red);
                    return msg;
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"同步软触发采集图像，获取异常，{ex.Message}", LogType.Sensor);
                return $"同步软触发采集图像，获取异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 清空图像列表数据
        /// </summary>
        /// <returns></returns>
        public string ClearReceiveImage()
        {
            try
            {
                while (Capture.ReceiveImage.Count > 0)
                {
                    Mat mat = null;
                    Capture.ReceiveImage.TryDequeue(out mat);
                }
                Log.SaveLog($"清空图像列表数据成功", LogType.Sensor, Color.Gray);
                return "OK";
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"清空图像数据异常异常，{ex.Message}", LogType.Sensor);
                return $"清空图像数据异常，{ex.Message}";
            }
        }
        #endregion

        #region 内部采集 
        /// <summary> 用于从驱动获取图像的缓存  </summary>
        private uint nBufSizeForDriver = 0;
        /// <summary> 用于从驱动获取图像的缓存  </summary>
        private IntPtr bufForDriver = IntPtr.Zero;
        /// <summary> 锁：用于从驱动获取图像的缓存 </summary>
        private readonly object lockBufForDriver = new object();
        /// <summary>
        /// 异步采集线程
        /// </summary>
        private void ReceiveThreadProcess()
        {
            int nRet;
            IntPtr convertDstBuf = IntPtr.Zero;
            while (IsGrabbing)
            {
                MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT();

                nRet = myCamera.MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000);
                if (nRet != MyCamera.MV_OK)
                {
                    Thread.Sleep(5);
                    continue;
                }

                //（指针）帧数据
                lock (lockBufForDriver)
                {
                    if (bufForDriver == IntPtr.Zero || stFrameInfo.stFrameInfo.nFrameLen > nBufSizeForDriver)
                    {
                        if (bufForDriver != IntPtr.Zero)
                        {
                            Marshal.Release(bufForDriver);
                            bufForDriver = IntPtr.Zero;
                        }

                        bufForDriver = Marshal.AllocHGlobal((Int32)stFrameInfo.stFrameInfo.nFrameLen);
                        if (bufForDriver == IntPtr.Zero)
                            return;

                        nBufSizeForDriver = stFrameInfo.stFrameInfo.nFrameLen;
                    }
                    CopyMemory(bufForDriver, stFrameInfo.pBufAddr, stFrameInfo.stFrameInfo.nFrameLen);
                }

                try
                {
                    var h = stFrameInfo.stFrameInfo.nHeight;
                    var w = stFrameInfo.stFrameInfo.nWidth;
                    switch (Capture.CaptureMode)
                    {
                        case CameraCaptureMode.连续采集指针模式:
                            {
                                if (Capture.HandlePictureBox != IntPtr.Zero)
                                {
                                    MyCamera.MV_DISPLAY_FRAME_INFO stDisplayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO();
                                    //stDisplayInfo = stFrameInfo.stFrameInfo;
                                    stDisplayInfo.hWnd = Capture.HandlePictureBox;
                                    stDisplayInfo.pData = stFrameInfo.pBufAddr;
                                    stDisplayInfo.nDataLen = stFrameInfo.stFrameInfo.nFrameLen;
                                    stDisplayInfo.nWidth = stFrameInfo.stFrameInfo.nWidth;
                                    stDisplayInfo.nHeight = stFrameInfo.stFrameInfo.nHeight;
                                    stDisplayInfo.enPixelType = stFrameInfo.stFrameInfo.enPixelType;
                                    myCamera.MV_CC_DisplayOneFrame_NET(ref stDisplayInfo);
                                }

                                break;
                            }
                        case CameraCaptureMode.软触发采集异步模式:
                        case CameraCaptureMode.硬触发采集异步模式:
                            {
                                if (Capture.ReceiveImage.Count > Capture.ReceiveImageMaxCount)
                                {
                                    Mat temp = null;
                                    Capture.ReceiveImage.TryDequeue(out temp);
                                    temp.Dispose();
                                }

                                Mat mat = null;
                                if (stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed ||
                                    stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
                                    mat = Mat.FromPixelData(h, w, MatType.CV_8UC3, stFrameInfo.pBufAddr);
                                else if (stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
                                    mat = Mat.FromPixelData(h, w, MatType.CV_8UC1, stFrameInfo.pBufAddr);
                                else if (
                                    stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8 ||
                                    stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8 ||
                                    stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8 ||
                                    stFrameInfo.stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8)
                                {
                                    //mat = Mat.FromPixelData(h, w, MatType.CV_8UC1, stFrameInfo.pBufAddr);

                                    MV_PIXEL_CONVERT_PARAM stConvertInfo = new MV_PIXEL_CONVERT_PARAM();
                                    stConvertInfo.nWidth = stFrameInfo.stFrameInfo.nWidth;
                                    stConvertInfo.nHeight = stFrameInfo.stFrameInfo.nHeight;
                                    stConvertInfo.enSrcPixelType = stFrameInfo.stFrameInfo.enPixelType;
                                    stConvertInfo.pSrcData = stFrameInfo.pBufAddr;
                                    stConvertInfo.nSrcDataLen = stFrameInfo.stFrameInfo.nFrameLen;
                                    stConvertInfo.nDstBufferSize = (UInt32)(3 * stConvertInfo.nWidth * stConvertInfo.nHeight);
                                    convertDstBuf = Marshal.AllocHGlobal((Int32)stConvertInfo.nDstBufferSize);
                                    stConvertInfo.pDstBuffer = convertDstBuf;
                                    stConvertInfo.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed;
                                    nRet = myCamera.MV_CC_ConvertPixelType_NET(ref stConvertInfo);

                                    mat = Mat.FromPixelData(h, w, MatType.CV_8UC3, stConvertInfo.pDstBuffer);
                                }

                                if (mat != null)
                                    Capture.ReceiveImage.Enqueue(mat.Clone());
                                break;
                            }
                    }
                }
                catch (Exception ex)
                {
                    Log.SaveError(ex, $"相机回调函数异常，{ex.Message}", LogType.Sensor);
                }
                finally
                {
                    if (convertDstBuf != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(convertDstBuf);
                        convertDstBuf = IntPtr.Zero;
                    }
                }

                if (stFrameInfo.pBufAddr != IntPtr.Zero)
                    myCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo);
            }
        }
        #endregion

        #region 相机详细操作

        /// <summary>
        /// 获取参数
        /// </summary>
        public string GetParam()
        {
            try
            {
                string _msg = "";
                int nRet;

                CameraHardwareParam read = Param.Read;

                //MyCamera.MVCC_INTVALUE intParam = new MyCamera.MVCC_INTVALUE();
                MyCamera.MVCC_ENUMVALUE enumParam = new MyCamera.MVCC_ENUMVALUE();
                MyCamera.MVCC_FLOATVALUE floatParam = new MyCamera.MVCC_FLOATVALUE();

                //图像模式
                nRet = myCamera.MV_CC_GetEnumValue_NET("PixelFormat", ref enumParam);
                if (nRet == MyCamera.MV_OK)
                    read.pixelFormat = (MyCamera.MvGvspPixelType)enumParam.nCurValue;
                AddError(nRet, ref _msg, "图像模式");

                //自动曝光 0关闭 1一次 2持续
                nRet = myCamera.MV_CC_GetEnumValue_NET("ExposureAuto", ref enumParam);
                if (nRet == MyCamera.MV_OK)
                    read.AutoExposure = (CameraParamAutoMode1)enumParam.nCurValue;
                AddError(nRet, ref _msg, "自动曝光");

                //曝光 5000
                nRet = myCamera.MV_CC_GetFloatValue_NET("ExposureTime", ref floatParam);
                if (nRet == MyCamera.MV_OK)
                    read.ExposureTime = floatParam.fCurValue;
                AddError(nRet, ref _msg, "曝光");

                //自动增益 0关闭 1一次 2持续
                nRet = myCamera.MV_CC_GetEnumValue_NET("GainAuto", ref enumParam);
                if (nRet == MyCamera.MV_OK)
                    read.AutoGain = (CameraParamAutoMode1)enumParam.nCurValue;
                AddError(nRet, ref _msg, "自动增益");

                //增益 0
                nRet = myCamera.MV_CC_GetFloatValue_NET("Gain", ref floatParam);
                if (nRet == MyCamera.MV_OK)
                    read.Gain = floatParam.fCurValue;
                AddError(nRet, ref _msg, "增益");

                //返回结果
                if (!string.IsNullOrWhiteSpace(_msg))
                {
                    Log.SaveLog($"获取相机参数（{_msg}）失败", LogType.Sensor, Color.Red);
                    return $"获取相机参数（{_msg}）失败";
                }
                else
                {
                    Log.SaveLog($"获取相机参数成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"获取相机参数异常，{ex.Message}", LogType.Sensor);
                return $"获取相机参数异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 设置参数
        /// </summary>
        public string SetParam(CameraHardwareParam cameraParam)
        {
            try
            {
                string _msg = "";
                int nRet;

                if (!cameraParam.ParamEnable)
                    return $"OK";

                //图像模式
                nRet = myCamera.MV_CC_SetEnumValue_NET("PixelFormat", (uint)cameraParam.pixelFormat);
                AddError(nRet, ref _msg, $"图像模式{cameraParam.PixelFormat.ToString()}");

                //自动曝光
                nRet = myCamera.MV_CC_SetEnumValue_NET("ExposureAuto", (uint)cameraParam.AutoExposure);
                AddError(nRet, ref _msg, "自动曝光");

                //曝光
                if (cameraParam.AutoExposure != CameraParamAutoMode1.连续)
                {
                    nRet = myCamera.MV_CC_SetFloatValue_NET("ExposureTime", cameraParam.ExposureTime);
                    AddError(nRet, ref _msg, "曝光");
                }

                //自动增益
                nRet = myCamera.MV_CC_SetEnumValue_NET("GainAuto", (uint)cameraParam.AutoGain);
                AddError(nRet, ref _msg, "自动增益");

                //增益
                if (cameraParam.AutoGain != CameraParamAutoMode1.连续)
                {
                    nRet = myCamera.MV_CC_SetFloatValue_NET("Gain", cameraParam.Gain);
                    AddError(nRet, ref _msg, "增益");
                }

                //返回结果
                if (!string.IsNullOrWhiteSpace(_msg))
                {
                    Log.SaveLog($"设置相机参数（{_msg}）失败", LogType.Sensor, Color.Red);
                    return $"设置相机参数（{_msg}）失败";
                }
                else
                {
                    Log.SaveLog($"设置相机参数成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"设置相机参数异常，{ex.Message}", LogType.Sensor);
                return $"设置相机参数异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 设置曝光参数
        /// </summary>
        /// <param name="cameraParam"> 投影仪标定 / 重建 </param>
        /// <returns></returns>
        public string SetExposureTime(CameraHardwareParam cameraParam)
        {
            try
            {
                string _msg = "";
                int nRet;

                //曝光
                nRet = myCamera.MV_CC_SetFloatValue_NET("ExposureTime", cameraParam.ExposureTime);
                AddError(nRet, ref _msg, "曝光");

                //返回结果
                if (!string.IsNullOrWhiteSpace(_msg))
                {
                    Log.SaveLog($"设置相机参数（{_msg}）失败", LogType.Sensor, Color.Red);
                    return $"设置相机参数（{_msg}）失败";
                }
                else
                {
                    Log.SaveLog($"设置相机参数成功", LogType.Sensor, Color.Gray);
                    return "OK";
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"设置相机参数异常，{ex.Message}", LogType.Sensor);
                return $"设置相机参数异常，{ex.Message}";
            }
        }

        /// <summary>
        /// 添加错误信息
        /// </summary>
        /// <param name="nRet"></param>
        /// <param name="msg"></param>
        /// <param name="errorMsg"></param>
        private void AddError(int nRet, ref string msg, string errorMsg)
        {
            if (nRet != MyCamera.MV_OK)
                msg = string.IsNullOrWhiteSpace(msg) ? errorMsg : msg + "、" + errorMsg;
        }
        #endregion

        #region 杂项
        /// <summary> 
        /// 显示错误信息
        /// </summary>
        private string ErrorMsg(int nErrorNum)
        {
            switch (nErrorNum)
            {
                case MyCamera.MV_E_HANDLE: return " Error or invalid handle ";
                case MyCamera.MV_E_SUPPORT: return " Not supported function ";
                case MyCamera.MV_E_BUFOVER: return " Cache is full ";
                case MyCamera.MV_E_CALLORDER: return " Function calling order error ";
                case MyCamera.MV_E_PARAMETER: return " Incorrect parameter ";
                case MyCamera.MV_E_RESOURCE: return " Applying resource failed ";
                case MyCamera.MV_E_NODATA: return " No data ";
                case MyCamera.MV_E_PRECONDITION: return " Precondition error, or running environment changed ";
                case MyCamera.MV_E_VERSION: return " Version mismatches ";
                case MyCamera.MV_E_NOENOUGH_BUF: return " Insufficient memory ";
                case MyCamera.MV_E_UNKNOW: return " Unknown error ";
                case MyCamera.MV_E_GC_GENERIC: return " General error ";
                case MyCamera.MV_E_GC_ACCESS: return " Node accessing condition error ";
                case MyCamera.MV_E_ACCESS_DENIED: return " No permission ";
                case MyCamera.MV_E_BUSY: return " Device is busy, or network disconnected ";
                case MyCamera.MV_E_NETER: return " Network error ";
                default: return $" Error code not recognized ({nErrorNum}) ";
            }
        }
        #endregion
    }
}
